
#include <sstream>
#include <iostream>

#include "SDL.h"

#include "ddc_error.h"
#include "ddc_input.h"
#include "ddc_internal.h"
#include "ddc_main.h"
#include "ddc_obj.h"
#include "ddc_time.h"

// DodocoInitError {

// } DodocoInitError

// Dodoco {

Dodoco *Dodoco::_dodoco = nullptr;

SDL_Surface *__gDodocoMainSurface = nullptr;
SDL_Renderer *__gDodocoMainRenderer = nullptr;
SDL_Window *__gDodocoMainWindow = nullptr;

DDC_Scene *Dodoco::NewScene(std::string sceneName, int *outSceneIndex) {
    DDC_Scene *scene = new DDC_Scene(sceneName);

    *outSceneIndex = _scenes.size();
    _scenes.push_back(scene);

    return scene;
}

DDC_Scene *Dodoco::NewScene(std::string sceneName) {
    int _;
    return this->NewScene(sceneName, &_);
}

int Dodoco::GetSceneIndex(std::string sceneName) {
    int size = _scenes.size();

    for (int i = 0; i < size; ++i) {
        if (_scenes[i]->name == sceneName)
            return i;
    }

    return -1;
}

DDC_Scene *Dodoco::GetScene(int index) {
    if (index < 0 || index >= _scenes.size())
        return nullptr;
    return _scenes[index];
}

/**
 * :return: > 0 if error occured.
 */
int Dodoco::RemoveScene(int index) {
    if (index < 0 || index >= _scenes.size())
        return 1;

    delete _scenes[index];
    _scenes.erase(_scenes.begin() + index);
}

Dodoco *Dodoco::GetDodocoWithInit(int windowWidth, int windowHeight, int maxFPS) {
    if (_dodoco == nullptr) {
        _dodoco = new Dodoco(windowWidth, windowHeight, maxFPS);
    }
    return _dodoco;
}

Dodoco *Dodoco::GetDodoco() {
    if (_dodoco == nullptr)
        std::cerr << "Warning: call Dodoco::GetDodoco() before initialize\n";
    return _dodoco;
}

Dodoco::Dodoco(int windowWidth, int windowHeight, int maxFPS) {
    SDL_Init(SDL_INIT_EVERYTHING);

    _windowWidth = windowWidth;
    _windowHeight = windowHeight;
    _maxFPS = maxFPS;

    __gDodocoMainWindow = SDL_CreateWindow("Dodoco",
                                           SDL_WINDOWPOS_UNDEFINED,
                                           SDL_WINDOWPOS_UNDEFINED,
                                           _windowWidth, _windowHeight,
                                           SDL_WINDOW_SHOWN);

    if (!__gDodocoMainWindow) {
        std::stringstream ss;
        ss << "cannot create window" << SDL_GetError();
        std::cerr << ss.str() << std::endl;
        throw DodocoInitError(ss.str());
    }

    __gDodocoMainRenderer = SDL_CreateRenderer(__gDodocoMainWindow, -1, SDL_RENDERER_ACCELERATED);
    if (!__gDodocoMainRenderer) {
        std::stringstream ss;
        ss << "cannot create renderer" << SDL_GetError();
        std::cerr << ss.str() << std::endl;
        throw DodocoInitError(ss.str());
    }

    __gDodocoMainSurface = SDL_GetWindowSurface(__gDodocoMainWindow);
    if (!__gDodocoMainSurface) {
        std::stringstream ss;
        ss << "cannot get surface" << SDL_GetError();
        std::cerr << ss.str() << std::endl;
        throw DodocoInitError(ss.str());
    }
}

int Dodoco::Run(DDC_Scene *initScene) {
    return _RunScene(initScene);
}

int Dodoco::_RunScene(DDC_Scene *scene) {
    int fpsDelay = 1000.0 / _maxFPS;
    int realDelay;

    Uint32 cBegin, cEnd, realEnd;

    _currentScene = scene;

    SDL_Event event;

scene_start:
    int logicWidth, logicHeight;
    GetWindowSize(&logicWidth, &logicHeight);

    if (_currentScene->screenLogicalSizeWidth != -1)
        logicWidth = _currentScene->screenLogicalSizeWidth;

    if (_currentScene->screenLogicalSizeHeight != -1)
        logicHeight = _currentScene->screenLogicalSizeHeight;

    SDL_RenderSetLogicalSize(__gDodocoMainRenderer, logicWidth, logicHeight);

    _currentScene->OnStart();
    for (;;) {
        cBegin = SDL_GetTicks();
        SDL_PollEvent(&event);

        if (event.type == SDL_QUIT)
            return 0;

        __currentEvent = &event;
        DDC_InputManager::__Update();

        if (doRenderClear)
            SDL_RenderClear(__gDodocoMainRenderer);

        _currentScene->Update();

        SDL_RenderPresent(__gDodocoMainRenderer);

        if (_sceneSwitched) {
            _sceneSwitched = false;
            _currentScene = _sceneToSwitch;
            _sceneToSwitch = nullptr;
            goto scene_start;
        }

        cEnd = SDL_GetTicks();
        realDelay = cEnd - cBegin;

        if (realDelay <= fpsDelay)
            SDL_Delay(fpsDelay - realDelay);

        realEnd = SDL_GetTicks();
        DDC_Time::__Update(realEnd - cBegin);
    }
}

int Dodoco::SwitchScene(int sceneIndex) {
    if (sceneIndex < 0 || sceneIndex >= _scenes.size()) {
        _DDC_ErrSetString("Invalid scene index.");
        return 1;
    }
    this->_sceneToSwitch = _scenes[sceneIndex];
    _sceneSwitched = true;
}

// } Dodoco
